using System;
using System.Threading;
using PaulRz.Threading;
using PaulRz.Util.Patterns;

namespace PaulRz.Util.Patterns
{
    public abstract class PipelineElement<TInput, TOutput> : ActiveObject 
        where TInput:class
        where TOutput:class 
    {
        protected PipelineElement(string name, ILogInterface logger, int joinTime) : base(name, logger, joinTime)
        {
        }

        #region Properties

        private SynchronizedQueue<TInput> inputQueue = null;
        public SynchronizedQueue<TInput> Input
        {
            get { return inputQueue; }
            set { inputQueue = value; }
        }

        private SynchronizedQueue<TOutput> outputQueue = null;
        public SynchronizedQueue<TOutput> Output
        {
            get { return outputQueue; }
            set { outputQueue = value; }
        }

        #endregion

        #region Methods

        private void StartPumping()
        {
            if (inputQueue!=null)
                inputQueue.Start();

            if (outputQueue!=null)
                outputQueue.Start();
        }
        
        private void StopPumping()
        {
            if (inputQueue!=null)
                inputQueue.Stop();

            if (outputQueue!=null)
                outputQueue.Stop();
        }
        
        protected abstract void ProcessCycle();
        
        protected override void Run()
        {
            StartPumping();            
            Logger.LogEvent(Severity.Debug, "Thread {0} has started.", Name);
            while(!IsStopping)
            {
                try
                {
                    ProcessCycle();
                }
                catch(ThreadAbortException)
                {
                    Logger.LogEvent(Severity.SystemStateChange, "Thread {0} has been aborted!", Name);
                    break;
                }
                    //TODO: it's bad but as soon as it's got some usage - it should be extended
                catch(PipeElementException)
                {
                    continue;
                }
                catch (Exception e)
                {
                    Logger.LogError("General error in {0}. Exception message : {1}", Name, e.ToString());
                }
            }
            StopPumping();
            Logger.LogEvent(Severity.Debug, "Thread {0} has stopped.", Name);
        }

        #endregion
    }

    public sealed class NullIOType
    {
        public static SynchronizedQueue<NullIOType> Queue = new SynchronizedQueue<NullIOType>(1);
    }
}
